﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using CoreEx.Common.Data;
using CoreEx.Database.Metadata;
using CoreEx.Database.Query;
using CoreEx.Database.Query.Linq;
using LinFu.IoC.Interfaces;
using LinFu.IoC;
using LinFu.Reflection;
using CoreEx.Common.Extensions;
namespace CoreEx.Common.Repository.Implementation
{
    
    public class Repository_New : IRepository, IInitialize<IServiceRequestResult>
    {
        private ITypeMappingRepository _mappingRepository;
        private IServiceContainer _serviceContainer;
        private IAliasMap _aliasMap;
        private IExpressionFactory _expressionFactory;
        private IExpressionTranslator _expressionTranslator;
        private IDictionary<MemberInfo,MemberInfo> _parentMembers = new Dictionary<MemberInfo, MemberInfo>();
        private IExpressionProjector _expressionProjector;

        /// <summary>
        /// Contains the expressions specified for each member        
        /// </summary>
        private IDictionary<MemberInfo, Expression> _memberExpressions = new Dictionary<MemberInfo, Expression>();

        /// <summary>
        /// Contains a list of ITable instances per type
        /// </summary>
        private IDictionary<Type, ITable> _tables = new Dictionary<Type, ITable>();


        private SelectQueryExpression CreateQuery(MemberInfo memberInfo, MemberInfo parentMemberInfo)
        {
            //Get the actual type that the memberinfo represents
            Type type = GetMemberType(memberInfo);

            if (!_parentMembers.ContainsKey(memberInfo))
            _parentMembers.Add(memberInfo,parentMemberInfo);

            //Create a new projection query
            var selectionQueryExpression = _expressionProjector.CreateProjection(_memberExpressions[memberInfo]);
            
            

            //We are at the root level where no parent expression exists
            if (_memberExpressions.ContainsKey(memberInfo) && parentMemberInfo == null)            
                //Apply the where expression to the root expression
                selectionQueryExpression.Where(_expressionTranslator.Translate(_memberExpressions[memberInfo]));
            else
                if (((PropertyInfo)memberInfo).PropertyType.IsEnumerable())
                    CreateJoinExpression(selectionQueryExpression,memberInfo);
                else
                    CreateExistsxpression(selectionQueryExpression,memberInfo);




            var relations = _memberExpressions.Where(c => c.Key.DeclaringType == type && ((PropertyInfo)c.Key).PropertyType.IsEnumerable());
            foreach (var relation in relations)
            {
                _aliasMap.Clear();
                CreateQuery(relation.Key,memberInfo);
            }

            return selectionQueryExpression;
        }

        private void EvaluateLoadExpression<T>(Expression<Func<T, object>> expression)
        {
            if (expression.Body.NodeType == ExpressionType.MemberAccess)
                _memberExpressions.Add(((MemberExpression)expression.Body).Member, expression);

         
        }


        private void CreateJoinExpression(SelectQueryExpression selectQueryExpression, MemberInfo memberInfo)
        {
            
        }

        private void CreateExistsxpression(SelectQueryExpression selectQueryExpression, MemberInfo memberInfo)
        {
            var test = CreateQuery(_parentMembers[memberInfo], null);
        }


        private SelectQueryExpression CreateProjection(Type type)
        {
            var query = _serviceContainer.GetService<IQuery>();

            var tableMappingInfo = _mappingRepository[type];
            var alias = _aliasMap.GetNextAlias(tableMappingInfo.TableInfo.TableName);
            var projection = tableMappingInfo.ColumnMappings.Values.Where(cm => cm.ColumnInfo != null).Select(
                cm => _expressionFactory.Column(alias, cm.ColumnInfo.ColumnName)).ToList();
            return query.Select(projection.ToArray()).From(e => e.Alias(e.Table(tableMappingInfo.TableInfo.TableName), alias));
        }






        private Type GetMemberType(MemberInfo memberInfo)
        {
            if (memberInfo is Type)
                return (Type) memberInfo;

            if (memberInfo is PropertyInfo)
                return ((PropertyInfo)memberInfo).PropertyType;

            return null;
        }


        



        public void LoadWith<T>(Expression<Func<T, object>> expression)
        {
            throw new System.NotImplementedException();
        }

        public IList<T> Find<T>(Expression<Func<T, bool>> expression, params Expression<Func<T, object>>[] loadWith)
        {
            _memberExpressions.Add(typeof(T),expression);
            loadWith.ForEach(EvaluateLoadExpression);
            CreateQuery(typeof(T),null);
            return null;
        }

        public IList<T> Find<T>(Expression<Func<T, bool>> expression)
        {
            throw new System.NotImplementedException();
        }

        public IList<T> FindAll<T>()
        {
            throw new System.NotImplementedException();
        }

        public IList<T> FindAll<T>(params Expression<Func<T, object>>[] loadWith)
        {
            throw new System.NotImplementedException();
        }

        public void Save(IList<IObjectState> changeRequests)
        {
            throw new System.NotImplementedException();
        }

        public void Initialize(IServiceRequestResult source)
        {
            _expressionFactory = source.Container.GetService<IExpressionFactory>();
           
            _mappingRepository = source.Container.GetService<ITypeMappingRepository>(source.ServiceName);
            _expressionTranslator = source.Container.GetService<IExpressionTranslator>(source.ServiceName);
            _aliasMap = source.Container.GetService<IAliasMap>();
            _expressionProjector = source.Container.GetService<IExpressionProjector>();

            _serviceContainer = source.Container;
        }

      
    }
}
